#include "discovery.h"
#include "generatetools.h"

#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/un.h>
#include <unistd.h>
#include <iostream>
#include <fcntl.h>
#include <string>

namespace KDiscovery {
const char *SERVER_UNIX_SOCKET_ADDR = "/tmp/kylin.deviceDiscover.socket";
const int BUF_LEN = 1024;

using namespace tlv;

static void sendRequest(int socket, RequestType type)
{
    DiscoveryDeviceInfo requestMsg;

    requestMsg.PutRequestValue(REQUEST, type);

    if (!requestMsg.Serialize()) {
        std::cerr << "requestMsg Serialize Failed !\n";
        return;
    }

    DiscoveryDeviceInfo request;
    request.PutObjectValue(REQUEST, requestMsg);

    if (!request.Serialize()) {
        std::cerr << "REQUEST Serialize Failed !\n";
        return;
    }

    if (socket) {
        if ((send(socket, request.GetSerializedBuffer(), request.GetSerializedBytes(), 0)) <= 0) {
            std::cerr << "send request fail" << std::endl;
        }
    }
}

static int SendSocketRequest(KDiscovery::RequestType type, const char * description) {
    std::cout << description << std::endl;

    int sock = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sock < 0) {
        std::cerr << "创建socket失败\n";
        return -1;
    }

    struct sockaddr_un address;  // 套接字的地址
    // 设置地址
    address.sun_family = AF_UNIX;
    strcpy(address.sun_path, SERVER_UNIX_SOCKET_ADDR);

    // 连接到服务器
    if (connect(sock, (struct sockaddr *)&address, sizeof(address)) < 0) {
        std::cerr << "连接到服务器失败" << std::endl;
        return -1;
    }
    sendRequest(sock, type);

    return sock;
}

static std::string JsonStringify(const KCommon::DiscoveryDeviceInfo &deviceInfo) {
    std::string str;
    str.append("{");
    str.append("\"uuid\":\"" + deviceInfo.uuid.toStdString() + "\",");
    str.append("\"ip\":\"" + deviceInfo.ip.toStdString() + "\",");
    str.append("\"port\":\"" + deviceInfo.port.toStdString() + "\",");
    str.append("\"deviceName\":\"" + deviceInfo.deviceName.toStdString() + "\",");
    str.append("\"deviceType\":\"" + std::to_string(deviceInfo.deviceType) + "\"");
    str.append("}");
    return str;
}

Discovery::Discovery()
{
}

Discovery::~Discovery() noexcept
{
   std::cout << "~Discovery()\n";
}

/**
 * @brief 发现设备，流程如下
 *
 * 1. 创建一个UNIX socket，并连接到服务器，然后向服务器发送START_DISCOVERY请求，
 * 最后等待服务器发送数据，并打印出来。
 * 2. 为了不阻塞，将socket设置为非阻塞模式。
 * 3. 此函数会等待10秒，等待服务器发送数据。
 * 4. 关闭socket。
 */
void Discovery::openSocket()
{
    m_socket = socket(AF_UNIX, SOCK_STREAM, 0);
    if (m_socket == -1) {
        std::cerr << "创建socket失败\n";
        return;
    }

    struct sockaddr_un address;  // 套接字的地址
    // 设置地址
    address.sun_family = AF_UNIX;
    strcpy(address.sun_path, SERVER_UNIX_SOCKET_ADDR);

    // 连接到服务器
    if (connect(m_socket, (struct sockaddr *)&address, sizeof(address)) < 0) {
        std::cerr << "连接到服务器失败" << std::endl;
        return;
    }

    // 设置socket为非阻塞模式
    int flags = fcntl(m_socket, F_GETFL, 0);
    fcntl(m_socket, F_SETFL, flags | O_NONBLOCK);

    // 循环等待数据到达
    while (true) {
        handleSocketData(m_socket);
    }

    close(m_socket);
}

void Discovery::startDiscovery()
{
    std::cout << "开始发现设备\n";

    if (m_socket == -1) {
        openSocket();
        GenerateTools::delayMs(1000);
    }

    SendSocketRequest(START_DISCOVERY, "开始发现设备");

    m_flags = true;
}

void Discovery::startSessionServer()
{
    SendSocketRequest(START_SESSION_SERVER, "开始会话服务");
}
// 循环等待数据到达
void Discovery::handleSocketData(int socket)
{
    char buffer[BUF_LEN];  // 接收数据的缓冲区
    fd_set read_fds;  // 保存可读的socket
    FD_ZERO(&read_fds);
    FD_SET(socket, &read_fds);
    struct timeval timeout;  // 设置超时时间为5秒
    timeout.tv_sec = 0;
    timeout.tv_usec = 500 * 1000;  // 500ms

    int select_ret = select(socket+1, &read_fds, NULL, NULL, &timeout);
    if (select_ret == -1) {
        perror("select出错");
        exit(EXIT_FAILURE);
    } else if (select_ret == 0) {
        // 没有数据到达，继续等待
        return;
    } else {
        if (FD_ISSET(socket, &read_fds)) {
            // 读取数据
            int bytes_received = recv(socket, buffer, BUF_LEN, 0);
            if (bytes_received <= 0) {
                perror("接收数据出错");
            } else {
                printf("接收到数据长度: %d\n", bytes_received);
                if (bytes_received < 30)
                    getSessionInfo(buffer, bytes_received);
                else
                    getDeviceInfo(buffer, bytes_received);
            }
        }
    }
}

void Discovery::getSessionInfo(char *buf, int len)
{
    // 将buf转为字符串
    std::string str((char *)buf, len);
    std::cout << "SessionInfo: " << str << std::endl;
}

void Discovery::getDeviceInfo(char *buf, int len)
{
    DiscoveryDeviceInfo parsedBoxes;
    if (!parsedBoxes.Parse((unsigned char *)buf, len)) {
        std::cout << "DiscoveryDeviceInfo Parse Failed !\n";
        return;
    }

    DiscoveryDeviceInfo parsedBox;
    if (!parsedBoxes.GetObjectValue(RESPONSE, parsedBox)) {
        std::cout << "GetObjectValue Failed !\n";
        return;
    }

    int value;
    if (!parsedBox.GetResponseValue(RESPONSE, value)) {
        std::cout << "GetResponseValue Failed !\n";
        return;
    }

    switch (value) {
        case DEVICE_INFO: {
            {
                char value[128];
                int length = 128;
                if (!parsedBox.GetUuidValue(DEVICE_UUID, value, length)) {
                    std::cout << "GetUuidValue Failed !\n";
                    return;
                }
               std::cout << "GetUuidValue Success " << value << "\n";
                m_uuid = value;
            }

            {
                int value;
                if (!parsedBox.GetDevTypeValue(DEVICE_TYPE, value)) {
                    std::cout << "GetDevTypeValue Failed !\n";
                    return;
                }
               std::cout << "GetDevTypeValue Success " << value << "\n";
                // if (value != KCommon::DeviceType::PC && value != KCommon::DeviceType::Phone) {
                //    std::cout << "return\n";
                //     return;
                // }
                m_devType = value;
            }

            {
                char value[128];
                int length = 128;
                if (!parsedBox.GetDevNameValue(DEVICE_NAME, value, length)) {
                    std::cout << "GetDevNameValue Failed !\n";
                    return;
                }
               std::cout << "GetDevNameValue Success " << value << "\n";
                m_deviceName = value;
            }

            {
                char value[128];
                int length = 128;
                if (!parsedBox.GetDevIpValue(DEVICE_IP, value, length)) {
                    std::cout << "GetDevIpValue Failed !\n";
                    return;
                }
               std::cout << "GetDevIpValue Success " << value << "\n";
                m_clientIp = value;
            }

            {
                int value;
                if (!parsedBox.GetDevPortValue(DEVICE_PORT, value)) {
                    std::cout << "GetDevPortValue Failed !\n";
                    return;
                }
               std::cout << "GetDevPortValue Success " << value << "\n";
                m_clientport = value;
            }

            if (!m_currentDeviceIpList.contains(m_clientIp)) {
                getClientInfo();
            }
            break;
        }
        default:
            break;
    }
}

void Discovery::getClientInfo()
{
    KCommon::DiscoveryDeviceInfo deviceInfo;

    deviceInfo.ip = m_clientIp;
    deviceInfo.port = m_clientport;
    deviceInfo.uuid = m_uuid;
    deviceInfo.deviceType = m_devType;
    deviceInfo.deviceName = m_deviceName;

    if (!m_discoveryDeviceInfo.contains(m_clientIp)) {
        m_discoveryDeviceInfo.insert(m_clientIp, deviceInfo);
        if (m_listener)
        {
            // info = std::string("{ \"uuid\": \"") + deviceInfo.uuid.toStdString() + "\", \"ip\": " + deviceInfo.ip.toStdString() + "\", \"port\": \"" +  deviceInfo.port.toStdString() + "\", \"deviceName\": \"" + deviceInfo.deviceName.toStdString() + "\", \"deviceType\": " + std::to_string(deviceInfo.deviceType) + " }";
            m_listener(JsonStringify(deviceInfo));
        }
    }
}

void Discovery::stopDiscovery()
{
   std::cout << "停止发现\n";

    int sock = socket(AF_UNIX, SOCK_STREAM, 0);
    if (sock == -1) {
        std::cerr << "socket create failed" << std::endl;
        return;
    }

    struct sockaddr_un address;
    address.sun_family = AF_UNIX;
    strcpy(address.sun_path, SERVER_UNIX_SOCKET_ADDR);

    if (connect(sock, (struct sockaddr *)&address, sizeof(address)) < 0) {
        std::cerr << "socket connect failed" << std::endl;
        return;
    }

    sendRequest(sock, STOP_DISCOVERY);

    m_flags = false;

    close(sock);
}

void Discovery::stopSessionServer()
{
    SendSocketRequest(STOP_SESSION_SERVER, "停止会话服务");
}

void Discovery::addDeviceListener(void (*callback)(std::string&&))
{
    m_listener = callback;
}

const char * Discovery::GetAllDeviceInfo()
{
    delete[] m_allDeviceInfo;
    std::string info = "[";
    if (!m_discoveryDeviceInfo.isEmpty()) {
        QMapIterator<QString, KCommon::DiscoveryDeviceInfo> it(m_discoveryDeviceInfo);
        while (it.hasNext()) {
            it.next();
            info += JsonStringify(it.value());
            info += ",";
        }
        info = info.substr(0, info.length() - 1);
    }

    info += "]";
    m_allDeviceInfo = new char[info.length() + 1];
    strcpy(m_allDeviceInfo, info.c_str());
    return m_allDeviceInfo;
}

void Discovery::sendSessionRequest(int type)
{
    SendSocketRequest(OPEN_SESSION, "开启会话");
    if (type == 0) {
        // 发送文件传输请求
        SendSocketRequest(REQUEST_FILE_TRANSFER, "发送文件传输会话请求");
    } else {
        // 发送投屏请求
        SendSocketRequest(RESPONSE_SCREEN_PROJECTION, "发送投屏会话请求");
    }
    GenerateTools::delayMs(1000);
    SendSocketRequest(OPEN_SESSION, "开启会话");
}
}  // namespace KDiscovery

